// CMultiListBox.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "Charplan.h"
#include "CMultiListBox.h"


// CMultiListBox

IMPLEMENT_DYNAMIC(CMultiListBox, CWnd)

CMultiListBox::CMultiListBox()
{
}

CMultiListBox::~CMultiListBox()
{
}


BEGIN_MESSAGE_MAP(CMultiListBox, CWnd)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
END_MESSAGE_MAP()



// CMultiListBox-Meldungshandler

size_t		CMultiListBox::AddLine(const CString& categorie)
{
	CString text = categorie;

	size_t pos = ms_data_title.size();

	if (pos>0)
	{
		size_t p = pos-1;
		while (p>0 && ms_data_title[p].IsEmpty()) p--;
		if (ms_data_title[p] == text)
		{
			text.Empty();
		}
	}

	ms_data_title.push_back(text);
	vector <struct_data_entry> temp;
	ms_data.push_back(temp);


	Invalidate();
	CheckScrollBar();


	return pos;
}


size_t		CMultiListBox::AddText(size_t line, const CString& txt1,const CString& txt2, COLORREF color /*=0*/,int data /*= 0*/)
{
	struct_data_entry entry;
	entry.hilite = false;
	entry.text1 = txt1;
	entry.text2 = txt2;
	entry.color = color;
	entry.user_data = data;

	ms_data[line].push_back(entry);

	return (int)(ms_data[line].size()-1);
}


//////////////////////////////////////////////////////////////////////////
void		CMultiListBox::SetItemData(size_t l, int subitem, int data)
{
	ms_data[l][subitem].user_data = data;
}


//////////////////////////////////////////////////////////////////////////
int			CMultiListBox::GetItemData(size_t l, int subitem)
{
	return 	ms_data[l][subitem].user_data;
}


//////////////////////////////////////////////////////////////////////////
void		CMultiListBox::SetChecked(size_t l, int s, bool checked /*= true*/)
{
	if (ms_data[l][s].hilite == checked) return;

	ms_data[l][s].hilite = checked;

	CRect r = GetItemRect((int)l,s);
	InvalidateRect(r);
}


//////////////////////////////////////////////////////////////////////////
bool		CMultiListBox::GetChecked(size_t l, int s)
{
	return 	ms_data[l][s].hilite;
}


//////////////////////////////////////////////////////////////////////////
void		CMultiListBox::ClearChecked()
{

	for (
			vector <vector <struct_data_entry>>::iterator iter_data = ms_data.begin();
				iter_data != ms_data.end(); iter_data++)
	{
		
		for (
			vector <struct_data_entry>::iterator iter_sub	 = iter_data->begin();
				iter_sub != iter_data->end(); iter_sub++)
		{
			iter_sub->hilite = false;
		}
	}
}



//////////////////////////////////////////////////////////////////////////
void	CMultiListBox::DeleteContents()
{
	ms_data.clear();
	ms_data_title.clear();
	mn_highlit_line = -1;
	mn_highlit_subitem = -1;

	Invalidate();
	CheckScrollBar();
}




//////////////////////////////////////////////////////////////////////////
void CMultiListBox::OnPaint()
{
	if (ms_data.empty())	return;

	CPaintDC dc(this); 


	LOGFONT lf;
	GetFont()->GetObject ( sizeof(LOGFONT), &lf );
	CFont font;
	font.CreateFontIndirect ( &lf );
	CFont* oldf = dc.SelectObject(&font);

	CFont font_big;
	lf.lfWeight = FW_SEMIBOLD;// FW_BOLD;
	font_big.CreateFontIndirect ( &lf );

	int line_height = GetLineHeight();


	CRect    rectWnd;      
	CRect    rectText;     

	GetWindowRect ( rectWnd );
	GetClientRect ( rectText );

	CRgn rgn;
 	CPoint ptOrg;
 	::GetViewportOrgEx(dc.m_hDC,&ptOrg);
	VERIFY(rgn.CreateRectRgnIndirect(rectText + ptOrg));
	dc.SelectClipRgn(&rgn);



	vector <CString>::const_iterator iter_title	 = ms_data_title.begin();
	vector <vector <struct_data_entry>>::const_iterator iter_data = ms_data.begin();

	int ix = 0;
	int iy = 0;

	// M.Jurkait [2/6/2006]
	// -------------------------
	// scroll
	// -------------------------
	SCROLLINFO si;
	GetScrollInfo( SB_VERT, &si, SIF_ALL );
	int top_line = si.nPos;
	int l = 3;
 	while (top_line>=l)
	{
		l = 2;
		if (!iter_title->IsEmpty()) l++;

		iter_title++;
		iter_data++;
		if (iter_data == ms_data.end()) goto finishdraw;

		top_line-=l;
		iy++;
	}


	int y = -top_line*(line_height+2);

	// Iterate through list and output each element.
	for (;iter_data != ms_data.end() && y<rectText.bottom; iter_title++, iter_data++)
	{
		if (!iter_title->IsEmpty())
		{
			CFont* oldf2 = dc.SelectObject(&font_big);

   			dc.FillSolidRect(0,y,rectText.Width(),16,GetSysColor(COLOR_3DLIGHT));

			dc.Draw3dRect(	4,y+line_height/2,   
				rectText.Width()-8, 4,  
				::GetSysColor(COLOR_3DSHADOW),
 				::GetSysColor(COLOR_3DHIGHLIGHT) );

			dc.SetBkColor(GetSysColor(COLOR_3DLIGHT));

			dc.TextOut(16,y+1,*iter_title,iter_title->GetLength());

			dc.SetBkColor(GetSysColor(COLOR_WINDOW));
			y += line_height+2;

			dc.SelectObject(oldf2);
		}
		else
		{
 			dc.Draw3dRect(	4,y,   
 				rectText.Width()-8, 2,  
 				::GetSysColor(COLOR_3DSHADOW),
 				::GetSysColor(COLOR_3DHIGHLIGHT) );
			y += 4;
		}

		vector <struct_data_entry>::const_iterator iter_sub	 = iter_data->begin();

		ix = 0;
		int x=0;
		int x_space = rectText.Width() / ((int)iter_data->size());
		for (;iter_sub != iter_data->end(); iter_sub++)
		{
			if (mn_highlit_line == iy && mn_highlit_subitem == ix)
			{
				if (iter_sub->hilite)
					dc.FillSolidRect(x,y,x_space,line_height*2-1,RGB(216,216,255));
				else
					dc.FillSolidRect(x,y,x_space,line_height*2-1,RGB(216,216,216));
			}
			else
			{
				if (iter_sub->hilite)
					dc.FillSolidRect(x,y,x_space,line_height*2-1,RGB(192,192,255));
			}

			dc.SetTextColor(iter_sub->color);
			dc.TextOut(x+1,y,iter_sub->text1,(int)iter_sub->text1.GetLength());
			dc.TextOut(x+1,y+line_height,iter_sub->text2,(int)iter_sub->text2.GetLength());

			dc.SetBkColor(GetSysColor(COLOR_WINDOW));
			dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
			x += x_space;

			ix ++;
		}
		y += line_height*2;
		iy++;
	}


	finishdraw:
		dc.SelectClipRgn(NULL);
		dc.SelectObject(oldf);
		font.DeleteObject();
}




BOOL CMultiListBox::OnEraseBkgnd(CDC* pDC)
{
	CRect    rectText;     
	GetClientRect ( rectText );

	pDC->FillSolidRect(rectText,GetSysColor(COLOR_WINDOW));

	return CWnd::OnEraseBkgnd(pDC);
}



void CMultiListBox::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	CheckScrollBar();

}



void	CMultiListBox::CheckScrollBar()
{

	// M.Jurkait [2/6/2006]
	// -------------------------
	// calc total item size
	// -------------------------
	int	 lines = 2*(int)ms_data.size();

	for (
		vector <CString>::const_iterator iter_title	 = ms_data_title.begin();
		iter_title != ms_data_title.end();iter_title++)
	{
		if (iter_title->GetLength()>0) lines++;
	}

	long height = lines * (GetLineHeight()+2);


	// M.Jurkait [2/6/2006]
	// -------------------------
	// check if scrollbars are needed
	// -------------------------
	CRect    rectWnd;      
	GetWindowRect ( rectWnd );

	if (rectWnd.Height() < height)
	{
		// set vertical scroll
		SCROLLINFO si;
		GetScrollInfo( SB_VERT, &si, SIF_ALL );


		si.cbSize	= sizeof( SCROLLINFO );
		si.nMin		= 0;
		si.nMax		= max( lines, 1 );
		si.nPos		= si.nPos;
		si.nPage	= rectWnd.Height()/(GetLineHeight()+2);
		si.fMask	= SIF_PAGE | SIF_RANGE | SIF_POS;
		if( si.nPos > si.nMax )
			si.nPos = si.nMax;

		ShowScrollBar(SB_VERT, TRUE);
		SetScrollInfo( SB_VERT, &si, TRUE );
	}
	else
		ShowScrollBar(SB_VERT, FALSE);

}




int	CMultiListBox::GetLineHeight()
{
	LOGFONT lf;
    CFont* fnt = GetFont();
    if (!fnt) return 15;
	fnt->GetObject ( sizeof(LOGFONT), &lf );
	int mn_line_high = lf.lfHeight;
	if (mn_line_high<0)	mn_line_high = -mn_line_high;

	return mn_line_high += 4;
}



void CMultiListBox::OnDestroy()
{
	CWnd::OnDestroy();
}




void CMultiListBox::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	SCROLLINFO si;
	GetScrollInfo( SB_VERT, &si, SIF_ALL );

	// TODO: optimize by just move untouched window contents
	switch( nSBCode )
	{
		case SB_LINEUP:
			if( si.nPos > si.nMin )						si.nPos--;
			break;

		case SB_LINEDOWN:
			if( si.nPos <= si.nMax - (int)si.nPage )	si.nPos++;;
			break;

		case SB_PAGEUP:
			si.nPos -= si.nPage;
			if( si.nPos < si.nMin )						si.nPos = si.nMin;
			break;

		case SB_PAGEDOWN:
			si.nPos += si.nPage;
			if( si.nPos > si.nMax )						si.nPos = si.nMax;
			break;

		case SB_TOP:
			si.nPos = si.nMin;
			break;

		case SB_BOTTOM:
			si.nPos = si.nMax;
			break;

		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			si.nPos = si.nTrackPos;
			break;

		default:
			break;
	}

	SetScrollPos( SB_VERT, si.nPos );
	Invalidate();
}



//////////////////////////////////////////////////////////////////////////
CRect	CMultiListBox::GetItemRect(int line, int subitem)
{
	CRect r;
	if (line<0) return r;


	int line_height = GetLineHeight();


	// M.Jurkait [2/6/2006]
	// -------------------------
	// get scroller
	// -------------------------
	vector <CString>::const_iterator iter_title	 = ms_data_title.begin();

	SCROLLINFO si;
	GetScrollInfo( SB_VERT, &si, SIF_ALL );
	int top_line = si.nPos;
	int l = 3;
	while (top_line>=l)
	{
		l = 2;
		if (!iter_title->IsEmpty()) l++;

		iter_title++;
		if (iter_title == ms_data_title.end()) return CRect();

		top_line-=l;
		line--;
	}

	r.top = -top_line*(line_height+2);





	// M.Jurkait [2/6/2006]
	// -------------------------
	// line
	// -------------------------
	if (line<0) return CRect();


	r.top += line*(line_height*2+4);
	line++;
	while (line > 0 && iter_title != ms_data_title.end())
	{
		if (!iter_title->IsEmpty())
		{
 			r.top += line_height-2;
		}
		line--;
		iter_title++;
	}






	r.bottom = r.top + line_height*2+4;



	// M.Jurkait [2/6/2006]
	// -------------------------
	// sub item
	// -------------------------
	CRect    rectText;     
	GetClientRect ( rectText );

	if (subitem == -1)
	{
		r.left = rectText.left;
		r.right = rectText.right;
	}
	else
	{
		int xspace = rectText.Width() / max(1,(int)ms_data[0].size());
		r.left = subitem*xspace;
		r.right = r.left+xspace;
	}

 
	return r;
}



bool	CMultiListBox::HitTest(CPoint point, int& line, int& subitem)
{
	line = -1;
	subitem = -1;

	int line_height = GetLineHeight();

	vector <CString>::const_iterator iter_title	 = ms_data_title.begin();

	// M.Jurkait [2/6/2006]
	// -------------------------
	// scroll
	// -------------------------
	SCROLLINFO si;
	GetScrollInfo( SB_VERT, &si, SIF_ALL );
	int top_line = si.nPos;
	int l = 3;
	while (top_line>=l)
	{
		l = 2;
		if (!iter_title->IsEmpty()) l++;

		iter_title++;
		if (iter_title == ms_data_title.end()) 
		{
			line = -1;
			return false;
		}

		top_line-=l;
		line++;
	}

	point.y += top_line*(line_height+2);


	while (point.y>0)
	{
		if (iter_title == ms_data_title.end())
		{
			line = -1;
			return false;
		}

		if (!iter_title->IsEmpty())
		{
			point.y -= line_height;
			if (point.y<0)
			{
				line = -1;
				return false;
			}
		}

		line++;
		point.y -= line_height*2+4;

		iter_title++;
	}




	// M.Jurkait [2/6/2006]
	// -------------------------
	// horizontal
	// -------------------------
	CRect    rectText;     
	GetClientRect ( rectText );
	int n = max(1,(int)ms_data[0].size());
	int xspace = rectText.Width() / n;
	subitem = point.x /xspace;
	if (subitem >=n)
	{
		subitem = -1;
		return false;
	}
	
	return true;
}


//////////////////////////////////////////////////////////////////////////
bool	CMultiListBox::SetHiLite(size_t l, int s)
{
	if (l!=mn_highlit_line || s!=mn_highlit_subitem)
	{
		CRect r = GetItemRect(mn_highlit_line,mn_highlit_subitem);
		InvalidateRect(r);

		mn_highlit_line = (int)l;
		mn_highlit_subitem = s;
		r = GetItemRect(mn_highlit_line,mn_highlit_subitem);
		InvalidateRect(r);

		return true;
	}

	return false;
}


//////////////////////////////////////////////////////////////////////////
void	CMultiListBox::GetHiLite(size_t& l, int& s)
{
	l = mn_highlit_line;
	s = mn_highlit_subitem;
}



//////////////////////////////////////////////////////////////////////////
BOOL CMultiListBox::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	OnVScroll(zDelta>0?SB_LINEUP:SB_LINEDOWN,abs(zDelta),NULL);
	return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}


//////////////////////////////////////////////////////////////////////////
void CMultiListBox::OnMouseMove(UINT nFlags, CPoint point)
{
	int l,s;
	if (HitTest(point,l,s))
	{
		if (SetHiLite(l,s))
		{
			GetOwner()->SendMessage(MLN_ITEM_HILITE,MAKEWPARAM(l,s));
		}
	}

	CWnd::OnMouseMove(nFlags, point);
}


//////////////////////////////////////////////////////////////////////////
void CMultiListBox::OnLButtonUp(UINT nFlags, CPoint point)
{
	int l,s;
	if (HitTest(point,l,s))
	{
		SetHiLite(l,s);

		GetOwner()->SendMessage(MLN_ITEM_CLICK,MAKEWPARAM(l,s));
	}

	CWnd::OnLButtonUp(nFlags, point);
}

